#include <iostream>
#include <string.h>

#include "FSM.hpp"

using namespace utility;

FSM::FSM(std::string n) {
    name = std::move(n);
    //logger = new Logger();
    Logger::instance();

    machine = nullptr;
    current = nullptr;
    noa = 0;
    actions = nullptr;
}

FSM::~FSM() {
    Logger::instance()->close();

    State *m = machine;
    while(m != nullptr) {
        Transition *t = m->transition;
        while(t != nullptr) {
            Transition *t1 = t->next;
            delete t;
            t = t1;
        }
        State *m1 = m->next;
        delete m;
        m = m1;
    }
    machine = nullptr;
    current = nullptr;
}

FSM &FSM::operator+(State *state) {
    if (machine == nullptr) {
        machine = state;
        current = state;
    }
    else {
        current->next = state;
        current = state;
    }

    return *this;
}

FSM &FSM::operator+(Transition *trans) {
    if (current == nullptr) return *this;

    if (current->transition == nullptr)
        current->transition = trans;
    else {
        Transition *t = current->transition;
        while (t->next != nullptr)
            t = t->next;
        t->next = trans;
    }

    return *this;
}

FSM& FSM::done() {
    State *m1, *m2;
    Transition *tran;

    char str[256];
    bool gotten;
    m1 = machine;
    while(m1 != nullptr) {
        tran = m1->transition;
        while(tran != nullptr) {
            gotten = false;
            m2 = machine;
            while(m2 != nullptr) {
                if (tran->state_code == m2->state_code) {
                    tran->state = m2;
                    gotten = true;
                    break;
                }
                m2 = m2->next;
            }
            if (!gotten) {
                sprintf(str, "State machine %s's state %d is not defined !", name.c_str(), tran->state_code);
                //LOG(ERROR, str);
                Logger::instance()->open("data\\edge\\errors.log");
                Logger::instance()->log(Logger::ERROR,__FILE__,__FILE__,str);
                exit(1);
            }
            tran = tran->next;
        }
        m1 = m1->next;
    }

    return *this;
}

bool FSM::set_current(int state_code) {
    State *m = machine;
    while(m != nullptr) {
        if (m->state_code == state_code) {
            current = m;
            return true;
        }
        m = m->next;
    }

    return false;
}

void FSM::set_actions(ActionTable *at, int n) {
    actions = at;
    noa = n;
}

int FSM::run(Event &event, int event_len) {
    if (strcmp(name.c_str(), "networking") != 0) {
        printf("\nFSM:%s, current state=%d\n", name.c_str(), current->state_code);
        printf("FSM:%s, Get a message. event_type=%d, event_code=%d, event_data=%d\n",
               name.c_str(), event.event_type, event.event_code, event.event_data);
    }
    Transition *tran = current->transition;
    bool trans_found = false;
    while(tran != nullptr) {
        if ((tran->event_type == event.event_type) &&
            (tran->event_code == event.event_code) &&
            (tran->event_data == -1 || (tran->event_data == event.event_data))) {
            trans_found = true;

            //run exit action for old state
            for(int i=0; i<noa; i++) {
                if (actions[i].action_code == current->exit_action) {
                    actions[i].action(&event, event_len, current->exit_action_extra_data);
                    break;
                }
            }

            //run transition action
            for(int i=0; i<noa; i++) {
                if (actions[i].action_code == tran->action_code) {
                    actions[i].action(&event, event_len, tran->action_extra_data);
                    break;
                }
            }

            set_current(tran->state_code);
            if (strcmp(name.c_str(), "networking") != 0)
                printf("FSM:%s, new state = %d\n", name.c_str(), tran->state_code);

            //run entry action for new state
            for(int i=0; i<noa; i++) {
                if (actions[i].action_code == current->entry_action) {
                    actions[i].action(&event, event_len, current->entry_action_extra_data);
                    break;
                }
            }

            //if a direct transition occurred in this state
            if (current->new_state_code != -1) {
                //run exit action for current state
                for(int i=0; i<noa; i++) {
                    if (actions[i].action_code == current->exit_action) {
                        actions[i].action(&event, event_len, current->exit_action_extra_data);
                        break;
                    }
                }
                set_current(current->new_state_code);
                //run entry action for new direct state
                for(int i=0; i<noa; i++) {
                    if (actions[i].action_code == current->entry_action) {
                        actions[i].action(&event, event_len, current->entry_action_extra_data);
                        break;
                    }
                }
            }
            break;
        }
        tran = tran->next;
    }
    if (!trans_found) {
        char ss[128];
        sprintf(ss, "FSM %s: transition not defined for state : %d, for event: event_type: %d, event_code: %d, event_data: %d!",
                name.c_str(), current->state_code, event.event_type, event.event_code, event.event_data);
        //LOG(WARNING, ss);
        Logger::instance()->open("data\\edge\\warmings.log");
        Logger::instance()->log(Logger::WARN,__FILE__,__FILE__,ss);
    }

    return 0;
}
